// Mediator for Nullsoft Winamp plugins. 
#include <pryn.h>
#include <windows.h>
#include "winamp.h"

extern "C" BOOL WINAPI SetDllDirectoryA (LPCSTR lpPathName);

HWND winampMainWindow;
ATOM winampWindowClass;
PrynState *winampState;

static PrynResult load (PrynMediator *mediator, const char *filename)
{
	PrynState *state = prynMediatorState (mediator);
	WinampInModule *(*getInModule2) ();
	PrynResult result = PrynResult_Success;
	
	HANDLE library = LoadLibraryA (filename);
	prynAssert (library, Failure);
	
	//winampWindowClass = RegisterClass (

	*(void **) &getInModule2 = GetProcAddress (library, "winampGetInModule2");
	if (getInModule2)
	{
		WinampInModule *module = getInModule2 ();
		if (!module) return PrynResult_Failure;
		
		module->mainWindow = winampMainWindow;
		module->instance = library;
		
		if (module->init) module->init ();
		//if (module->about) module->about (0);
		
		prynPrintLiteral (state, "Load: ");
		prynPrintZ (state, module->description);
		prynPrintLiteral (state, "\n");
	}

success:
	if (0)
	{
	failure:;
	}
	
	return result;
}

static LRESULT CALLBACK windowProcedure (HWND window, UINT message, WPARAM a, LPARAM b)
{
	switch (message)
	{
		case WM_WA_IPC:
			switch (b)
			{
				case IPC_GET_API_SERVICE:
					return (LRESULT) new WinampAPIService ();
					//return 1;
				
				default:
					prynPrintLiteral (winampState, "Unknown Winamp IPC message: ");
					prynPrintInteger (winampState, b);
					prynPrintLiteral (winampState, ", option ");
					prynPrintInteger (winampState, a);
					prynPrintLiteral (winampState, "\n");
			}
	}
	
	return DefWindowProcA (window, message, a, b);
}

PrynResult enable (PrynMediator *mediator)
{
	PrynState *state = prynMediatorState (mediator);
	
	SetErrorMode (0);
	SetDllDirectoryA ("Libraries\\Winamp"); // FIXME - should use library path plus "\\Winamp".
	SetDllDirectoryA ("C:\\Program Files\\Winamp");
	
	winampState = state;
	
	HINSTANCE instance = GetModuleHandleA (0);
	WNDCLASS info;
	
	info.style = 0;
	info.lpfnWndProc = &windowProcedure;
	info.cbClsExtra = 0;
	info.cbWndExtra = 0;
	info.hInstance = instance;
	info.hIcon = 0;
	info.hCursor = 0;
	info.hbrBackground = 0;
	info.lpszMenuName = 0;
	info.lpszClassName = "Pryn Winamp Mediator Class";
	
	winampWindowClass = RegisterClass (&info);
	if (!winampWindowClass) return PrynResult_Failure;
	
	winampMainWindow = CreateWindowA ((char *) winampWindowClass, "Pryn Winamp Mediator Window", WS_OVERLAPPED, 0, 0, 100, 100, 0, 0, instance, 0);
	if (!winampMainWindow) return PrynResult_Failure;
	
	//return load (mediator, "C:\\Program Files\\Winamp\\Plugins\\in_wave.dll");
	
	return PrynResult_Success;
}

PrynLibraryEntryPoint (PrynLibrary *library)
{
	PrynResult result = PrynResult_Success;
	PrynMediator *mediator;
	
	prynSetLibraryNameLiteral (library, "Winamp plugin mediator");
	prynSetLibraryDescriptionLiteral (library,"This allows access to Nullsoft Winamp plugins.");
	
	result = prynCreateMediator (library, &mediator, prynStringLiteral ("Pryn.Winamp"));
	if (result < 0) return result;
	
	prynSetMediatorNameLiteral (mediator, "Winamp plugin");
	prynSetMediatorDescriptionLiteral (mediator, "This allows use of Nullsoft Winamp plugins.");
	prynSetMediatorEnableFunction (mediator, &enable);
	
	return result;
}

void prynPrintHex (PrynState *state, unsigned int value, int digits)
{
	char buffer [16];
	int radix = 16;
	
	if (digits > sizeof (buffer))
		digits = sizeof (buffer);
	
	for (int digit = 0, divisor = 1; digit < digits; digit ++, divisor *= radix)
		buffer [digit] = "0123456789abcdef" [(value / divisor) % radix];
	
	prynPrint (state, prynStringFrom (buffer, digits));
}

void prynPrintGuid (PrynState *state, GUID guid)
{
	prynPrintLiteral (state, "{");
	prynPrintHex (state, guid.Data1, 8);
	prynPrintLiteral (state, "-");
	prynPrintHex (state, guid.Data2, 4);
	prynPrintLiteral (state, "-");
	prynPrintHex (state, guid.Data3, 4);
	prynPrintLiteral (state, "-");
	prynPrintHex (state, guid.Data4 [0], 2);
	prynPrintHex (state, guid.Data4 [1], 2);
	prynPrintLiteral (state, "-");
	for (int i = 0; i < 6; i ++)
		prynPrintHex (state, guid.Data4 [i + 2], 2);
	prynPrintLiteral (state, "}");
}

int WinampAPIService::_dispatch (int message, void *result, void **parameters, int parameterCount)
{
	switch (message)
	{
		case API_SERVICE_SERVICE_GETSERVICEBYGUID:
			if (parameterCount != 1) return 0;
			GUID *guid = (GUID *) parameters [0];
			
			prynPrintLiteral (winampState, "What the fuck: ");
			prynPrintGuid (winampState, *guid);
			prynPrintLiteral (winampState, "\n");
			
			return 0;
			
		default:
			prynPrintLiteral (winampState, "Dispatch ");
			prynPrintInteger (winampState, message);
			prynPrintLiteral (winampState, ", parameter count ");
			prynPrintInteger (winampState, parameterCount);
			prynPrintLiteral (winampState, "\n");
			return 0;
	}
}

void *operator new (size_t size)
{
	return prynAllocate (winampState, size);
}

void operator delete (void *pointer)
{
	prynFree (winampState, pointer);
}

